package com.datasource;

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.bind.RelaxedDataBinder;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

/**
 * 动态数据源注册
 *
 * @author linyukun
 */
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceRegister.class);

    // 数据源配置信息
    private PropertyValues dataSourcePropertyValues;
    // 默认数据源
    private DataSource defaultDataSource;
    // 动态数据源
    private Map<String, DataSource> dynamicDataSources = new HashMap<>();


    /**
     * 加载多数据源配置
     * Environment 是用来读取application.properties中的内容
     */
    @Override
    public void setEnvironment(Environment env) {
        //读取application.properties中以jdbc.开头的属性
        RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "jdbc.");
        String dsPrefixs = propertyResolver.getProperty("datasources");//这个配置在application.properties文件中
        String onlyTestDevDatasources = propertyResolver.getProperty("only.test.dev.datasources");//这个配置在application.properties文件中
        String onlyProdDataSources = propertyResolver.getProperty("only.prod.datasources");//这个配置在application.properties文件中

        // 获取当前加载的是哪个配置文件
        RelaxedPropertyResolver propertyResolver1 = new RelaxedPropertyResolver(env, "spring.profiles.");
        String active = propertyResolver1.getProperty("active");

        //region 设置 通用的数据源 默认数据源
        for (String dsPrefix : dsPrefixs.split(",")) {// 多个数据源
            Map<String, Object> map = propertyResolver.getSubProperties(dsPrefix + ".");
            DataSource ds = initDataSource(map);
            // 设置默认数据源
            if ("library".equals(dsPrefix)) {//默认数据源
                defaultDataSource = ds;
            } else {
                dynamicDataSources.put(dsPrefix, ds);
            }
            dataBinder(ds, env);
        }
        //endregion

        //region 增加 测试环境和开发环境的数据源
        if ( !"prod".equals(active) && StringUtils.isNotBlank(onlyTestDevDatasources)) {
            for (String testDevDataSource : onlyTestDevDatasources.split(",")) {// 多个数据源
                Map<String, Object> map = propertyResolver.getSubProperties(testDevDataSource + ".");
                DataSource ds = initDataSource(map);
                dynamicDataSources.put(testDevDataSource, ds);
            }
        }
        //endregion

        //region 增加 正式环境使用的数据源
        if("prod".equals(active) && StringUtils.isNotBlank(onlyProdDataSources)){
            for (String prodDataSource : onlyProdDataSources.split(",")) {// 多个数据源
                Map<String, Object> map = propertyResolver.getSubProperties(prodDataSource + ".");
                DataSource ds = initDataSource(map);
                dynamicDataSources.put(prodDataSource, ds);
            }
        }
        //endregion

    }

    /**
     * 初始化数据源
     *
     * @param map
     * @return
     */
    public DataSource initDataSource(Map<String, Object> map) {

        String url = map.get("url").toString();
        String driverClassName = null;
        /*判断是MySQL还是sqlserver*/
        if (url.toLowerCase().contains("mysql")) {
            driverClassName = "com.mysql.jdbc.Driver";
        } else if (url.toLowerCase().contains("postgresql")) {
            driverClassName = "org.postgresql.Driver";
        } else {
            driverClassName = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
        }
        String username = map.get("username").toString();
        String password = map.get("password").toString();
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driverClassName);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        //配置最大连接
        dataSource.setMaxActive(300);
        //配置初始连接
        dataSource.setInitialSize(20);
        //配置最小连接
        dataSource.setMinIdle(10);
        //连接等待超时时间
        dataSource.setMaxWait(60000);
        //间隔多久进行检测,关闭空闲连接
        dataSource.setTimeBetweenEvictionRunsMillis(60);//睡眠时间原本是60000
        //一个连接最小生存时间
        dataSource.setMinEvictableIdleTimeMillis(300000);
        //连接等待超时时间 单位为毫秒 缺省启用公平锁，
        //并发效率会有所下降， 如果需要可以通过配置useUnfairLock属性为true使用非公平锁
        dataSource.setUseUnfairLock(true);
        //用来检测是否有效的sql
        dataSource.setValidationQuery("select 'x'");
        dataSource.setTestWhileIdle(true);
        //申请连接时执行validationQuery检测连接是否有效，配置为true会降低性能
        dataSource.setTestOnBorrow(false);
        //归还连接时执行validationQuery检测连接是否有效，配置为true会降低性能
        dataSource.setTestOnReturn(false);
        //打开PSCache,并指定每个连接的PSCache大小启用poolPreparedStatements后，
        //PreparedStatements 和CallableStatements 都会被缓存起来复用，
        //即相同逻辑的SQL可以复用一个游标，这样可以减少创建游标的数量。
        dataSource.setPoolPreparedStatements(true);
        dataSource.setMaxOpenPreparedStatements(20);
        try {

            //资料 https://blog.csdn.net/mrczr/article/details/78903468
            //		配置文件需要加这句话 spring.datasource.druid.wall.config.multiStatementAllow=true
            //		RelaxedPropertyResolver wallConfigResolver = new RelaxedPropertyResolver(env, "spring.datasource.druid.wall.config.");
            //		wallConfig = new WallConfig();
            //		String multiStatementAllow = wallConfigResolver.getProperty("multiStatementAllow");
            //		wallConfig.setMultiStatementAllow(new Boolean(multiStatementAllow));


            // 配置sql监控的filter https://blog.csdn.net/m0_51390535/article/details/121441973
            // wall过滤器参考：https://wenku.baidu.com/view/4709062415fc700abb68a98271fe910ef12dae0b.html
            //dataSource.setFilters("stat,wall,log4j");//
            dataSource.setFilters("stat,log4j");// 如果加了wall过滤器 需要补上 wallConfig不然没办法批量更新 wallConfig配置文件有

//			List<Filter> filters = dataSource.getProxyFilters();
//			for(Filter filter : filters){
//				if(filter instanceof WallFilter){
//					((WallFilter) filter).setConfig(wallConfig);
//				}
//			}

            dataSource.init();
        } catch (SQLException e) {
            throw new RuntimeException("druid datasource init fail");
        }
		/*String dsType = map.get("dsType").toString();
		Class<DataSource> dataSourceType;
		DataSource dataSource = null;
		try {
			dataSourceType = (Class<DataSource>) Class.forName(dsType);

			dataSource = DataSourceBuilder.create().driverClassName(driverClassName).url(url)
					.username(username).password(password).type(dataSourceType).build();;
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}*/
        return dataSource;
    }

    /**
     * 加载数据源配置信息
     * 绑定数据源的其他配置
     * 目前看代码好像用不到
     * http://t.zoukankan.com/jay-wu-p-12030374.html
     * https://blog.csdn.net/u013419838/article/details/114396588
     * https://www.jianshu.com/p/a1fbfc4f9e12
     * Relaxed Binding机制
     * @param dataSource
     * @param env
     */
    private void dataBinder(DataSource dataSource, Environment env) {
        // 创建一个粘合剂
        RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);
        // Set ignored*
        dataBinder.setIgnoreNestedProperties(false);// false
        dataBinder.setIgnoreInvalidFields(false);// false
        dataBinder.setIgnoreUnknownFields(true);// true
        if (dataSourcePropertyValues == null) {
            //从Environment中获取属性
            Map<String, Object> values = new RelaxedPropertyResolver(env, "datasource").getSubProperties(".");//目前配置文件没有这些东西
            // 将属性MAP转换为MutablePropertyValues
            dataSourcePropertyValues = new MutablePropertyValues(values);
        }
        // 绑定 将数据源的其它配置绑定到DataSource中
        dataBinder.bind(dataSourcePropertyValues);
    }

    /**
     * 注册数据源been
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
        // 将主数据源添加到更多数据源中
        targetDataSources.put("dataSource", defaultDataSource);
        // 添加更多数据源
        targetDataSources.putAll(dynamicDataSources);

        // 创建DynamicDataSource
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();//自定义bean
        beanDefinition.setBeanClass(DynamicDataSource.class);//设置bean的类型 DynamicDataSource是我们自己的类
        beanDefinition.setSynthetic(true);
        MutablePropertyValues mpv = beanDefinition.getPropertyValues();
        mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);//默认的数据源 AbstractRoutingDataSource这个类下面的getConnection方法里面写了
        mpv.addPropertyValue("targetDataSources", targetDataSources);
        registry.registerBeanDefinition("dataSource", beanDefinition);

        logger.info("多数据源注册成功");
    }

}

